<?php

/**
 *
 * Created by PhpStorm.
 * User: 29631
 * Date: 2017-12-18
 * Time: 9:53
 */
class protocol_reader
{
    function type_dynsize($b, $p)
    {
        $bb = unpack('C', substr($b, $p, 1))[1];
        ++$p;
        $n = ($bb & 0XC0) >> 6;
        $s = ($bb & 0X3F);
        while ($n > 0) {
            $bb = unpack('C', substr($b, $p, 1))[1];
            ++$p;
            $s = ($s << 8) | $bb;
            --$n;
        }
        return [$s, $p];
    }

    function type_number($f, $n, $b, $p)
    {
        return [unpack($f, substr($b, $p, $n))[1], $p + $n];
    }

    function type_int64($b, $p, $valMax = 0)
    {
        return $this->type_number('q', 8, $b, $p);
    }

    function type_uint64($b, $p, $valMax = 0)
    {
        return $this->type_number('Q', 8, $b, $p);
    }

    function type_double($b, $p, $valMax = 0)
    {
        return $this->type_number('d1', 8, $b, $p);
    }

    function type_float($b, $p, $valMax = 0)
    {
        return $this->type_number('f', 4, $b, $p);
    }

    function type_int32($b, $p, $valMax = 0)
    {
        return $this->type_number('i', 4, $b, $p);
    }

    function type_uint32($b, $p, $valMax = 0)
    {
        return $this->type_number('I', 4, $b, $p);
    }

    function type_int16($b, $p, $valMax = 0)
    {
        return $this->type_number('s', 2, $b, $p);
    }

    function type_uint16($b, $p, $valMax = 0)
    {
        return $this->type_number('S', 2, $b, $p);
    }

    function type_int8($b, $p, $valMax = 0)
    {
        return $this->type_number('c', 1, $b, $p);
    }

    function type_uint8($b, $p, $valMax = 0)
    {
        return $this->type_number('C', 1, $b, $p);
    }

    function type_bool($b, $p, $valMax = 0)
    {
        return ($b[$p] == chr(0)) ? [false, $p + 1] : [true, $p + 1];
    }

    function type_string($b, $p, $valMax = 0)
    {
        list($l, $p) = $this->type_dynsize($b, $p);
        if ($l > $valMax) {
            throw new Exception();
        }
        return [substr($b, $p, $l), $p + $l];
    }

    function type_enum($b, $p, $valMax = 0)
    {
        list($e, $p) = $this->type_uint8($b, $p, 0);
        if ($e > $valMax) {
            throw new Exception();
        }
        return [$e, $p];
    }
	
	function type_enum16($b, $p, $valMax = 0)
    {
        list($e, $p) = $this->type_uint16($b, $p, 0);
        if ($e > $valMax) {
            throw new Exception();
        }
        return [$e, $p];
    }

    function type_binary($b, $p, $valMax = 0)
    {
        return $this->type_string($b, $p, $valMax);
    }

    function type_mid($b, $p)
    {
        return $this->type_uint16($b, $p, 0);
    }

    function read($b, $p, $func, $arrMax, $valMax = 0)
    {
        if ($arrMax > 0) {
            list($s, $p) = $this->type_dynsize($b, $p);
            if ($s > $arrMax) {
                throw new Exception();
            } else {
                $ary = array();
                while ($s > 0) {
                    list($i, $p) = call_user_func_array($func, array($b, $p, $valMax));
                    array_push($ary, $i);
                    --$s;
                }
                return [$ary, $p];
            }
        } else {
            return call_user_func_array($func, array($b, $p, $valMax));
        }

    }

}